/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2011 Varnish Software AS
 * All rights reserved.
 *
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "config.h"

#include <sys/types.h>
#include <sys/socket.h>

#include <limits.h>
#include <netdb.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include "vsb.h"
#include "vss.h"

#include "vcc_priv.h"
#include "vcc_compile.h"
#include "libvarnish.h"

struct auth {
	VTAILQ_ENTRY(auth)      list;
	struct token            *name;
	char			*vgcname;
};

static void
vcc_ParseAuthDef(struct vcc *tl, int serial, const char *vgcname)
{
	struct fld_spec *fs;
	struct token *t_field;
	struct token *t_type = NULL;
	struct token *t_realm = NULL;
	struct token *t_path = NULL;
	struct vsb *vsb;

	Fh(tl, 1, "\n#define VGC_auth_%s %d\n", vgcname, tl->nauth);

	fs = vcc_FldSpec(tl,
	    "!type",
	    "?realm",
	    "?path",
	    NULL);

	SkipToken(tl, '{');

	vsb = VSB_new_auto();
	AN(vsb);
	tl->fb = vsb;

	Fb(tl, 0, "\nstatic const struct vrt_vauth vgc_auth_priv_%s = {\n",
	    vgcname);

	Fb(tl, 0, "\t.name = \"%.*s", PF(tl->t_auth));
	if (serial >= 0)
		Fb(tl, 0, "[%d]", serial);
	Fb(tl, 0, "\",\n");

	while (tl->t->tok != '}') {
		vcc_IsField(tl, &t_field, fs);
		ERRCHK(tl);
		if (vcc_IdIs(t_field, "type")) {
			ExpectErr(tl, CSTR);
			assert(tl->t->dec != NULL);
			t_type = tl->t;
			vcc_NextToken(tl);
			SkipToken(tl, ';');
		} else if (vcc_IdIs(t_field, "path")) {
			ExpectErr(tl, CSTR);
			assert(tl->t->dec != NULL);
			t_path = tl->t;
			vcc_NextToken(tl);
			SkipToken(tl, ';');
		} else if (vcc_IdIs(t_field, "realm")) {
			ExpectErr(tl, CSTR);
			assert(tl->t->dec != NULL);
			t_realm = tl->t;
			vcc_NextToken(tl);
			SkipToken(tl, ';');
		} else {
			ErrInternal(tl);
			return;
		}
	}
	vcc_FieldsOk(tl, fs);
	ERRCHK(tl);
	AN(t_type);
	if (!strcmp(t_type->dec, "basic")) {
		if (t_path == NULL) {
			VSB_printf(tl->sb,
			    ".path isn't set for auth ");
			vcc_ErrToken(tl, t_type);
			VSB_printf(tl->sb, " at\n");
			vcc_ErrWhere(tl, t_type);
			return;
		}
		Fb(tl, 0, "\t.type = VAUTH_BASIC,\n");
		Fb(tl, 0, "\t.path = ");
		EncToken(tl->fb, t_path);
		Fb(tl, 0, ",\n");
		if (t_realm != NULL) {
			Fb(tl, 0, "\t.realm = ");
			EncToken(tl->fb, t_realm);
			Fb(tl, 0, ",\n");
		}
		ExpectErr(tl, '}');
		Fb(tl, 0, "};\n");
	} else {
		VSB_printf(tl->sb, "Unknown backend type ");
		vcc_ErrToken(tl, t_type);
		VSB_printf(tl->sb, " at\n");
		vcc_ErrWhere(tl, t_type);
		return;
	}
	vcc_NextToken(tl);

	tl->fb = NULL;
	AZ(VSB_finish(vsb));
	Fh(tl, 0, "%s", VSB_data(vsb));
	VSB_delete(vsb);

	Fi(tl, 0, "\tVRT_auth_init(cli, VCL_conf.auth, \"basic\",\n"
	    "\t    VGC_auth_%s, &vgc_auth_priv_%s);\n", vgcname, vgcname);
	Ff(tl, 0, "\tVRT_auth_fini(cli, VGCAUTH(%s));\n", vgcname);
	tl->nauth++;
}

static void
vcc_DefAuth(struct vcc *tl, const struct token *nm)
{
	struct symbol *sym;

	sym = VCC_GetSymbolTok(tl, nm, SYM_AUTH);
	AN(sym);
	if (sym->ndef > 0) {
		VSB_printf(tl->sb, "Auth %.*s redefined\n", PF(tl->t));
		vcc_ErrWhere(tl, nm);
		return;
	}
	sym->fmt = AUTH;
	sym->eval = vcc_Eval_Auth;
	sym->ndef++;
}

static void
vcc_ParseAuth(struct vcc *tl)
{
	struct auth *a;
	char vgcname[BUFSIZ];

	a = TlAlloc(tl, sizeof *a);
	a->name = tl->t_auth;
	vcc_DefAuth(tl, tl->t_auth);
	ERRCHK(tl);
	sprintf(vgcname, "_%.*s", PF(a->name));
	a->vgcname = TlAlloc(tl, strlen(vgcname) + 1);
	strcpy(a->vgcname, vgcname);

	vcc_ParseAuthDef(tl, -1, vgcname);
	ERRCHK(tl);

	VTAILQ_INSERT_TAIL(&tl->auths, a, list);
}

void
vcc_Auth(struct vcc *tl)
{
	struct token *t_first;

	t_first = tl->t;
	vcc_NextToken(tl);		/* ID: director | backend */

	vcc_ExpectCid(tl);		/* ID: name */
	ERRCHK(tl);
	tl->t_auth = tl->t;
	vcc_NextToken(tl);

	assert(vcc_IdIs(t_first, "auth"));
	vcc_ParseAuth(tl);
	if (tl->err) {
		VSB_printf(tl->sb,
		    "\nIn %.*s specification starting at:\n", PF(t_first));
		vcc_ErrWhere(tl, t_first);
		return;
	}
	tl->t_auth = NULL;
}
